from fastapi import APIRouter, HTTPException, UploadFile, File, Response, BackgroundTasks, Request
from fastapi.responses import StreamingResponse
from you_get import common as you_get
from apscheduler.schedulers.background import BackgroundScheduler
import logging
import subprocess
import json
import time
import os
import re
import asyncio
import tempfile
import shutil
import ssl
import urllib.request
import socket
from typing import List, Dict, Any, Optional, Union
from pydantic import BaseModel
from starlette.background import BackgroundTask

# 定义cookies文件存储路径
COOKIES_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "cookies")

router = APIRouter()

YOU_GET_AVAILABLE = True
try:
    import you_get
except ImportError:
    YOU_GET_AVAILABLE = False

# 禁用SSL验证（解决SSL连接错误）
def disable_ssl_verification():
    # 创建不验证SSL证书的上下文
    ssl_context = ssl._create_unverified_context()
    # 替换默认的HTTPS处理器
    opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ssl_context))
    urllib.request.install_opener(opener)

# 设置网络请求超时
socket.setdefaulttimeout(30)  # 设置30秒超时

def check_youget() -> bool:
    try:
        result = subprocess.run(['you-get', '--version'], capture_output=True, text=True)
        return result.returncode == 0
    except Exception as e:
        logging.error(f"you-get检查失败: {str(e)}")
        return False

# 初始化后台任务调度器
scheduler = BackgroundScheduler()

# 定义请求模型
class VideoUrlRequest(BaseModel):
    url: str

class BilibiliRequest(BaseModel):
    url: str
    # 移除弹幕下载选项
    # danmaku: bool = False

class YoutubeRequest(BaseModel):
    url: str
    subtitles: bool = False

@router.get("/download")
async def download_video(url: str):
    if not YOU_GET_AVAILABLE:
        raise HTTPException(status_code=501, detail="视频下载功能不可用")
    
    try:
        # 设置you-get下载参数
        sys_argv = ['you-get']
        
        # 添加URL
        sys_argv.append(url)
        
        # 添加信息参数，先获取视频信息
        info_argv = ['you-get', '--info', url]
        
        # 获取视频信息
        import sys
        from io import StringIO
        original_stdout = sys.stdout
        sys.stdout = StringIO()
        
        try:
            # 禁用SSL验证
            disable_ssl_verification()
            
            # 执行获取信息（带重试机制）
            max_retries = 3
            retry_count = 0
            success = False
            last_error = None
            
            while retry_count < max_retries and not success:
                try:
                    # 捕获SystemExit异常，这是you-get在出错时抛出的异常
                    try:
                        you_get.main(info_argv)
                        success = True
                    except SystemExit as se:
                        # 检查是否是B站API变更导致的JSON解析错误
                        if "TypeError: the JSON object must be str, bytes or bytearray, not NoneType" in sys.stdout.getvalue():
                            error_msg = "B站接口可能已更新，无法解析视频信息，请等待软件更新"
                            logging.error(f"B站API解析错误: {sys.stdout.getvalue()}")
                            raise Exception(error_msg)
                        else:
                            # 其他SystemExit错误
                            error_msg = f"you-get解析失败，退出代码: {se.code if hasattr(se, 'code') else '未知'}"
                            logging.error(f"you-get解析失败: {sys.stdout.getvalue()}")
                            raise Exception(error_msg)
                except (urllib.error.URLError, ssl.SSLError, socket.timeout) as e:
                    retry_count += 1
                    last_error = e
                    logging.warning(f"获取视频信息出错，正在重试 ({retry_count}/{max_retries}): {str(e)}")
                    # 重试前等待一段时间
                    time.sleep(2)
                except Exception as e:
                    # 其他错误不重试
                    last_error = e
                    logging.error(f"获取视频信息失败: {str(e)}")
                    break
            
            # 如果所有重试都失败
            if not success:
                error_msg = f"获取视频信息失败: {str(last_error)}"
                if isinstance(last_error, (urllib.error.URLError, ssl.SSLError)):
                    error_msg = f"网络连接错误: {str(last_error)}，请检查网络连接或稍后重试"
                raise Exception(error_msg)
                
            info_output = sys.stdout.getvalue()
            sys.stdout = original_stdout
            
            # 解析视频信息
            video_title = None
            site_name = None
            
            for line in info_output.split('\n'):
                if line.startswith('title:'):
                    video_title = line.split(':', 1)[1].strip()
                elif line.startswith('site:'):
                    site_name = line.split(':', 1)[1].strip()
                    
            if not video_title:
                video_title = f"video_{int(time.time())}"
                
            # 返回视频信息和下载指令
            return {
                "success": True, 
                "message": "获取视频信息成功，请点击下载按钮开始下载", 
                "title": video_title,
                "site": site_name,
                "type": "universal",
                "url": url,
                "info": info_output
            }
            
        except Exception as e:
            sys.stdout = original_stdout
            logging.error(f"获取视频信息失败: {str(e)}")
            raise HTTPException(status_code=500, detail=f"获取视频信息失败: {str(e)}")
            
    except Exception as e:
        logging.error(f"视频下载出错: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/analyze")
async def analyze_url(request: VideoUrlRequest):
    if not YOU_GET_AVAILABLE:
        raise HTTPException(status_code=501, detail="视频解析功能不可用")
    
    try:
        # 记录解析请求
        logging.info(f"解析URL: {request.url}")
        
        # 设置you-get参数，使用info模式获取视频信息
        sys_argv = ['you-get', '--info', request.url]
        
        # 重定向you-get的输出以捕获信息
        import sys
        from io import StringIO
        original_stdout = sys.stdout
        sys.stdout = StringIO()
        
        # 禁用SSL验证
        disable_ssl_verification()
        
        # 执行解析（带重试机制）
        original_argv = sys.argv
        sys.argv = sys_argv
        
        # 最多重试3次
        max_retries = 3
        retry_count = 0
        success = False
        last_error = None
        
        while retry_count < max_retries and not success:
            try:
                # 捕获SystemExit异常，这是you-get在出错时抛出的异常
                try:
                    you_get.main()
                    success = True
                except SystemExit as se:
                    # 检查是否是B站API变更导致的JSON解析错误
                    if "TypeError: the JSON object must be str, bytes or bytearray, not NoneType" in output:
                        error_msg = "B站接口可能已更新，无法解析视频信息，请等待软件更新"
                        logging.error(f"B站API解析错误: {output}")
                        raise Exception(error_msg)
                    else:
                        # 其他SystemExit错误
                        error_msg = f"you-get解析失败，退出代码: {se.code if hasattr(se, 'code') else '未知'}"
                        logging.error(f"you-get解析失败: {output}")
                        raise Exception(error_msg)
            except (urllib.error.URLError, ssl.SSLError, socket.timeout) as e:
                retry_count += 1
                last_error = e
                logging.warning(f"解析URL出错，正在重试 ({retry_count}/{max_retries}): {str(e)}")
                # 重试前等待一段时间
                time.sleep(2)
            except Exception as e:
                # 其他错误不重试
                last_error = e
                logging.error(f"解析URL失败: {str(e)}")
                break
        
        # 如果所有重试都失败
        if not success:
            error_msg = f"解析失败: {str(last_error)}"
            if isinstance(last_error, (urllib.error.URLError, ssl.SSLError)):
                error_msg = f"网络连接错误: {str(last_error)}，请检查网络连接或稍后重试"
            return {"success": False, "message": error_msg}
            
        sys.argv = original_argv
        
        # 获取输出并解析
        output = sys.stdout.getvalue()
        sys.stdout = original_stdout
        
        # 记录解析结果
        logging.info(f"URL解析结果: \n{output}")
        
        # 解析输出信息，提取媒体信息
        media_info = []
        site = None
        title = None
        current_stream = None
        
        for line in output.split('\n'):
            line = line.strip()
            
            # 提取网站信息
            if line.startswith('site:'):
                site = line.split(':', 1)[1].strip()
            
            # 提取标题信息
            elif line.startswith('title:'):
                title = line.split(':', 1)[1].strip()
            
            # 提取流信息
            elif line.startswith('- itag:') or line.startswith('- format:'):
                if current_stream:
                    media_info.append(current_stream)
                current_stream = {'type': 'video'}
            
            # 提取容器格式
            elif line.startswith('container:') and current_stream:
                current_stream['container'] = line.split(':', 1)[1].strip()
            
            # 提取质量信息
            elif line.startswith('quality:') and current_stream:
                current_stream['quality'] = line.split(':', 1)[1].strip()
            
            # 提取大小信息
            elif line.startswith('size:') and current_stream:
                current_stream['size'] = line.split(':', 1)[1].strip()
        
        # 添加最后一个流信息
        if current_stream:
            media_info.append(current_stream)
        
        return {
            "success": True, 
            "message": "解析成功", 
            "site": site,
            "title": title,
            "media": media_info
        }
    except Exception as e:
        logging.error(f"解析URL出错: {str(e)}")
        return {"success": False, "message": f"解析失败: {str(e)}"}

@router.post("/youtube")
async def download_youtube_video(request: YoutubeRequest):
    if not YOU_GET_AVAILABLE:
        raise HTTPException(status_code=501, detail="YouTube下载功能不可用")
    
    try:
        # 记录下载请求
        logging.info(f"YouTube下载请求: {request.url}, 字幕: {request.subtitles}")
        
        # 设置you-get下载参数
        sys_argv = ['you-get']
        
        # 添加字幕下载选项
        if request.subtitles:
            sys_argv.append('--caption')
            
        # 添加URL
        sys_argv.append(request.url)
        
        # 添加信息参数，先获取视频信息
        info_argv = ['you-get', '--info', request.url]
        
        # 获取视频信息
        import sys
        from io import StringIO
        original_stdout = sys.stdout
        sys.stdout = StringIO()
        
        try:
            you_get.main(info_argv)
            info_output = sys.stdout.getvalue()
            sys.stdout = original_stdout
            
            # 解析视频信息
            video_title = None
            for line in info_output.split('\n'):
                if line.startswith('title:'):
                    video_title = line.split(':', 1)[1].strip()
                    break
                    
            if not video_title:
                video_title = f"youtube_video_{int(time.time())}"
                
            # 返回视频信息和下载指令
            return {
                "success": True, 
                "message": "获取视频信息成功，请点击下载按钮开始下载", 
                "title": video_title,
                "type": "youtube",
                "url": request.url,
                "subtitles": request.subtitles,
                "info": info_output
            }
            
        except Exception as e:
            sys.stdout = original_stdout
            logging.error(f"获取视频信息失败: {str(e)}")
            raise HTTPException(status_code=500, detail=f"获取视频信息失败: {str(e)}")
            
    except Exception as e:
        logging.error(f"YouTube下载出错: {str(e)}")
        return {"success": False, "message": f"下载失败: {str(e)}"}

@router.post("/bilibili")
async def download_bilibili_video(request: BilibiliRequest):
    if not YOU_GET_AVAILABLE:
        raise HTTPException(status_code=501, detail="Bilibili下载功能不可用")
    
    try:
        # 记录下载请求
        logging.info(f"Bilibili下载请求: {request.url}")
        
        # 设置you-get下载参数
        sys_argv = ['you-get']
        
        # 检查是否有B站cookies文件
        cookies_file_path = os.path.join(COOKIES_DIR, "bilibili.txt")
        if os.path.exists(cookies_file_path) and os.path.isfile(cookies_file_path):
            # 检查文件大小确保不是空文件
            if os.path.getsize(cookies_file_path) > 0:
                sys_argv.extend(['--cookies', cookies_file_path])
                logging.info(f"使用cookies文件: {cookies_file_path}")
            else:
                logging.warning(f"B站cookies文件存在但为空: {cookies_file_path}")
        else:
            # 尝试在临时目录查找
            temp_cookies_path = os.path.join(tempfile.gettempdir(), "bilibili.txt")
            if os.path.exists(temp_cookies_path) and os.path.isfile(temp_cookies_path) and os.path.getsize(temp_cookies_path) > 0:
                sys_argv.extend(['--cookies', temp_cookies_path])
                logging.info(f"使用临时目录中的cookies文件: {temp_cookies_path}")
            else:
                logging.warning("未找到有效的B站cookies文件，可能无法下载高清视频")
        
        # 始终添加--playlist参数以支持多P视频下载
        sys_argv.append('--playlist')
            
        # 移除弹幕下载选项
        # if request.danmaku:
        #     sys_argv.append('--danmaku')
            
        # 添加URL
        sys_argv.append(request.url)
        
        # 添加信息参数，先获取视频信息
        info_argv = ['you-get', '--info', request.url]
        
        # 如果有cookies，也添加到info命令
        if os.path.exists(cookies_file_path) and os.path.isfile(cookies_file_path) and os.path.getsize(cookies_file_path) > 0:
            info_argv.extend(['--cookies', cookies_file_path])
            logging.info(f"使用cookies文件获取视频信息: {cookies_file_path}")
        else:
            # 尝试在临时目录查找
            temp_cookies_path = os.path.join(tempfile.gettempdir(), "bilibili.txt")
            if os.path.exists(temp_cookies_path) and os.path.isfile(temp_cookies_path) and os.path.getsize(temp_cookies_path) > 0:
                info_argv.extend(['--cookies', temp_cookies_path])
                logging.info(f"使用临时目录中的cookies文件获取视频信息: {temp_cookies_path}")
        
        # 获取视频信息
        import sys
        from io import StringIO
        original_stdout = sys.stdout
        sys.stdout = StringIO()
        
        try:
            # 禁用SSL验证
            disable_ssl_verification()
            
            # 执行获取信息（带重试机制）
            original_argv = sys.argv
            sys.argv = info_argv
            
            # 最多重试3次
            max_retries = 3
            retry_count = 0
            success = False
            last_error = None
            
            while retry_count < max_retries and not success:
                try:
                    # 捕获SystemExit异常，这是you-get在出错时抛出的异常
                    try:
                        you_get.main()
                        success = True
                    except SystemExit as se:
                        # 检查是否是B站API变更导致的JSON解析错误
                        if "TypeError: the JSON object must be str, bytes or bytearray, not NoneType" in info_output:
                            error_msg = "B站接口可能已更新，无法解析视频信息，请等待软件更新"
                            logging.error(f"B站API解析错误: {info_output}")
                            raise Exception(error_msg)
                        else:
                            # 其他SystemExit错误
                            error_msg = f"you-get解析失败，退出代码: {se.code if hasattr(se, 'code') else '未知'}"
                            logging.error(f"you-get解析失败: {info_output}")
                            raise Exception(error_msg)
                except (urllib.error.URLError, ssl.SSLError, socket.timeout) as e:
                    retry_count += 1
                    last_error = e
                    logging.warning(f"获取Bilibili视频信息出错，正在重试 ({retry_count}/{max_retries}): {str(e)}")
                    # 重试前等待一段时间
                    time.sleep(2)
                except Exception as e:
                    # 其他错误不重试
                    last_error = e
                    logging.error(f"获取Bilibili视频信息失败: {str(e)}")
                    break
            
            # 如果所有重试都失败
            if not success:
                error_msg = f"获取视频信息失败: {str(last_error)}"
                if isinstance(last_error, (urllib.error.URLError, ssl.SSLError)):
                    error_msg = f"网络连接错误: {str(last_error)}，请检查网络连接或稍后重试"
                raise Exception(error_msg)
                
            sys.argv = original_argv
            info_output = sys.stdout.getvalue()
            sys.stdout = original_stdout
            
            # 解析视频信息
            video_title = None
            for line in info_output.split('\n'):
                if line.startswith('title:'):
                    video_title = line.split(':', 1)[1].strip()
                    break
                    
            if not video_title:
                video_title = f"bilibili_video_{int(time.time())}"
                
            # 返回视频信息和下载指令
            return {
                "success": True, 
                "message": "获取视频信息成功，请点击下载按钮开始下载", 
                "title": video_title,
                "type": "bilibili",
                "url": request.url,
                "info": info_output
            }
            
        except Exception as e:
            sys.stdout = original_stdout
            logging.error(f"获取视频信息失败: {str(e)}")
            raise HTTPException(status_code=500, detail=f"获取视频信息失败: {str(e)}")
            
    except Exception as e:
        logging.error(f"Bilibili下载出错: {str(e)}")
        return {"success": False, "message": f"下载失败: {str(e)}"}

# 定义cookies文件存储路径
COOKIES_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "cookies")



# 确保cookies目录存在
try:
    os.makedirs(COOKIES_DIR, exist_ok=True)
    logging.info(f"确保cookies目录存在: {COOKIES_DIR}")
    
    # 检查目录权限
    if not os.access(COOKIES_DIR, os.W_OK):
        logging.warning(f"cookies目录没有写入权限: {COOKIES_DIR}")
        
    # 检查B站cookies文件是否存在
    bilibili_cookies_path = os.path.join(COOKIES_DIR, "bilibili.txt")
    if os.path.exists(bilibili_cookies_path):
        if os.path.getsize(bilibili_cookies_path) > 0:
            logging.info(f"B站cookies文件已存在: {bilibili_cookies_path}")
        else:
            logging.warning(f"B站cookies文件存在但为空: {bilibili_cookies_path}")
    else:
        logging.info(f"B站cookies文件不存在，需要上传: {bilibili_cookies_path}")
        
except Exception as e:
    logging.error(f"创建cookies目录失败: {str(e)}")
    # 使用临时目录作为备选
    COOKIES_DIR = os.path.join(tempfile.gettempdir(), "libertool_cookies")
    os.makedirs(COOKIES_DIR, exist_ok=True)
    logging.warning(f"使用临时目录作为cookies存储路径: {COOKIES_DIR}")

@router.post("/upload-cookies")
async def upload_bilibili_cookies(file: UploadFile = File(...)):
    global COOKIES_DIR
    try:
        # 检查文件类型
        if not file.filename.endswith(('.txt', '.json')):
            logging.warning(f"上传的cookies文件格式可能不正确: {file.filename}")
            # 不阻止上传，但记录警告
        
        # 保存上传的cookies文件
        content = await file.read()
        
        # 检查内容是否为空
        if not content or len(content) < 10:  # 至少需要一些基本内容
            logging.error("上传的cookies文件内容为空或过小")
            return {"success": False, "message": "上传失败: cookies文件内容为空或无效"}
        
        # 创建cookies文件名（使用bilibili.txt作为标准名称）
        cookies_file_path = os.path.join(COOKIES_DIR, "bilibili.txt")
        
        # 确保目录存在
        os.makedirs(os.path.dirname(cookies_file_path), exist_ok=True)
        
        # 保存cookies内容到文件
        try:
            with open(cookies_file_path, "wb") as f:
                f.write(content)
            
            # 验证文件是否成功写入
            if os.path.exists(cookies_file_path) and os.path.getsize(cookies_file_path) > 0:
                logging.info(f"成功保存cookies文件: {cookies_file_path}")
                return {"success": True, "message": "Cookies文件上传成功，将用于下载高清B站视频"}
            else:
                logging.error(f"cookies文件保存失败或为空: {cookies_file_path}")
                return {"success": False, "message": "上传失败: 无法保存cookies文件"}
        except PermissionError:
            # 尝试使用临时目录
            temp_cookies_path = os.path.join(tempfile.gettempdir(), "bilibili.txt")
            logging.warning(f"原始路径写入失败，尝试使用临时路径: {temp_cookies_path}")
            
            with open(temp_cookies_path, "wb") as f:
                f.write(content)
                
            # 更新全局cookies路径
            COOKIES_DIR = tempfile.gettempdir()
            logging.info(f"已更新cookies目录为临时目录: {COOKIES_DIR}")
            
            return {"success": True, "message": "Cookies文件已保存到临时目录，将用于下载高清B站视频"}
    except Exception as e:
        logging.error(f"Cookies上传出错: {str(e)}")
        return {"success": False, "message": f"上传失败: {str(e)}"}


@router.get("/stream-download")
async def stream_download(url: str, type: str = "bilibili", subtitles: bool = False):
    """流式下载视频到客户端浏览器，实时显示下载进度"""
    if not YOU_GET_AVAILABLE:
        raise HTTPException(status_code=501, detail="视频下载功能不可用")
    
    try:
        # 创建临时目录用于下载
        temp_dir = tempfile.mkdtemp(prefix="video_download_")
        logging.info(f"创建临时下载目录: {temp_dir}")
        
        # 设置you-get下载参数
        sys_argv = ['you-get']
        
        # 根据视频类型添加特定参数
        if type == "bilibili":
            # 检查是否有B站cookies文件
            cookies_file_path = os.path.join(COOKIES_DIR, "bilibili.txt")
            if os.path.exists(cookies_file_path):
                sys_argv.extend(['--cookies', cookies_file_path])
                logging.info(f"使用cookies文件: {cookies_file_path}")
            else:
                logging.warning("未找到B站cookies文件，可能无法下载高清视频")
                
            # 始终添加--playlist参数以支持多P视频下载
            sys_argv.append('--playlist')
                
            # 移除弹幕下载选项
        elif type == "youtube" and subtitles:
            sys_argv.append('--caption')
        
        # 设置输出目录
        sys_argv.extend(['-o', temp_dir])
        
        # 添加URL
        sys_argv.append(url)
        
        # 创建进度跟踪管道
        progress_pipe = asyncio.Queue()
        
        # 定义下载任务
        async def download_task():
            try:
                import sys
                from io import StringIO
                
                # 重定向标准输出以捕获进度信息
                original_stdout = sys.stdout
                progress_capture = StringIO()
                sys.stdout = progress_capture
                
                # 设置参数并执行下载
                sys.argv = sys_argv
                
                # 启动下载前发送初始进度
                await progress_pipe.put({"status": "start", "progress": 0, "message": "开始下载..."})
                
                # 禁用SSL验证
                disable_ssl_verification()
                
                # 执行下载（带重试机制）
                original_argv = sys.argv
                sys.argv = sys_argv
                
                # 最多重试3次
                max_retries = 3
                retry_count = 0
                success = False
                last_error = None
                
                while retry_count < max_retries and not success:
                    try:
                        # 捕获SystemExit异常，这是you-get在出错时抛出的异常
                        try:
                            you_get.main()
                            success = True
                            # 检查是否是多P视频
                            output = progress_capture.getvalue()
                            if "This is a multipart video" in output:
                                logging.info("检测到多P视频，确保使用--playlist参数下载所有分P")
                                await progress_pipe.put({"status": "multipart", "progress": 50, "message": "检测到多P视频，正在下载所有分P..."})
                        except SystemExit as se:
                            # 检查是否是B站API变更导致的JSON解析错误
                            if "TypeError: the JSON object must be str, bytes or bytearray, not NoneType" in str(progress_capture.getvalue()):
                                error_msg = "B站接口可能已更新，无法解析视频信息，请等待软件更新"
                                logging.error(f"B站API解析错误: {progress_capture.getvalue()}")
                                raise Exception(error_msg)
                            else:
                                # 其他SystemExit错误
                                error_msg = f"you-get下载失败，退出代码: {se.code if hasattr(se, 'code') else '未知'}"
                                logging.error(f"you-get下载失败: {progress_capture.getvalue()}")
                                raise Exception(error_msg)
                    except (urllib.error.URLError, ssl.SSLError, socket.timeout) as e:
                        retry_count += 1
                        last_error = e
                        logging.warning(f"下载出错，正在重试 ({retry_count}/{max_retries}): {str(e)}")
                        error_msg = f"网络错误，正在重试 ({retry_count}/{max_retries})..."
                        logging.warning(error_msg)
                        await progress_pipe.put({"status": "retrying", "progress": 0, "message": error_msg})
                        # 重试前等待一段时间
                        await asyncio.sleep(2)
                    except Exception as e:
                        # 其他错误不重试
                        last_error = e
                        logging.error(f"下载失败: {str(e)}")
                        break
                
                # 如果所有重试都失败
                if not success:
                    error_msg = f"下载失败: {str(last_error)}"
                    if isinstance(last_error, (urllib.error.URLError, ssl.SSLError)):
                        error_msg = f"网络连接错误: {str(last_error)}，请检查网络连接或稍后重试"
                    raise Exception(error_msg)
                
                sys.argv = original_argv
                
                # 获取输出并恢复标准输出
                output = progress_capture.getvalue()
                sys.stdout = original_stdout
                
                # 记录下载完成
                logging.info(f"视频下载完成: {url}\n{output}")
                
                # 查找下载的文件
                downloaded_files = []
                for root, _, files in os.walk(temp_dir):
                    for file in files:
                        file_path = os.path.join(root, file)
                        # 确保文件路径有效且文件存在
                        if os.path.exists(file_path) and os.path.isfile(file_path):
                            downloaded_files.append(file_path)
                        else:
                            logging.warning(f"跳过无效文件路径: {file_path}")
                
                logging.info(f"下载目录中的文件: {downloaded_files}")
                
                # 检查是否需要合并视频和音频
                video_file = None
                audio_file = None
                output_file = None
                
                # 识别视频和音频文件
                for file in downloaded_files:
                    # 再次检查文件是否真实存在（双重保障）
                    if not os.path.exists(file):
                        logging.warning(f"文件路径存在但文件不存在: {file}")
                        continue
                        
                    # 使用小写扩展名进行比较，避免大小写问题
                    file_lower = file.lower()
                    if file_lower.endswith(('.mp4', '.flv', '.webm')):
                        video_file = file
                        logging.info(f"找到视频文件: {video_file}")
                    elif file_lower.endswith(('.m4a', '.mp3', '.aac')):
                        audio_file = file
                        logging.info(f"找到音频文件: {audio_file}")
                
                # 如果同时有视频和音频文件，需要合并
                if video_file and audio_file and os.path.exists(video_file) and os.path.exists(audio_file):
                    await progress_pipe.put({"status": "merging", "progress": 95, "message": "正在合并视频和音频..."})
                    
                    # 合并文件名
                    output_file = os.path.join(temp_dir, f"merged_{os.path.basename(video_file)}")
                    
                    # 使用ffmpeg合并
                    merge_cmd = [
                        'ffmpeg', '-i', video_file, '-i', audio_file, 
                        '-c:v', 'copy', '-c:a', 'aac', '-strict', 'experimental',
                        output_file
                    ]
                    
                    process = await asyncio.create_subprocess_exec(
                        *merge_cmd,
                        stdout=asyncio.subprocess.PIPE,
                        stderr=asyncio.subprocess.PIPE
                    )
                    
                    await process.communicate()
                    
                    if process.returncode == 0 and os.path.exists(output_file):
                        # 合并成功，使用合并后的文件
                        await progress_pipe.put({"status": "merged", "progress": 98, "message": "视频和音频合并成功"})
                        logging.info(f"合并成功，使用合并后的文件: {output_file}")
                        return output_file
                    else:
                        # 合并失败，使用原始视频文件
                        await progress_pipe.put({"status": "merge_failed", "progress": 98, "message": "视频和音频合并失败，将下载原始文件"})
                        logging.warning(f"合并失败，使用原始视频文件: {video_file}")
                        return video_file
                else:
                    # 只有一个文件，直接返回
                    result_file = None
                    if video_file and os.path.exists(video_file):
                        result_file = video_file
                        logging.info(f"使用视频文件: {result_file}")
                    elif audio_file and os.path.exists(audio_file):
                        result_file = audio_file
                        logging.info(f"使用音频文件: {result_file}")
                    elif downloaded_files:
                        # 尝试找到第一个存在的文件
                        for file in downloaded_files:
                            if os.path.exists(file) and os.path.isfile(file):
                                # 检查文件大小，跳过空文件
                                if os.path.getsize(file) > 0:
                                    result_file = file
                                    logging.info(f"使用下载的文件: {result_file}")
                                    break
                                else:
                                    logging.warning(f"跳过空文件: {file}")
                    
                    if not result_file:
                        # 记录更详细的错误信息
                        error_msg = "未找到有效的下载文件"
                        if downloaded_files:
                            error_msg += f"，找到了 {len(downloaded_files)} 个文件但都不可用"
                            logging.error(f"下载文件列表: {downloaded_files}")
                        else:
                            error_msg += "，下载目录为空"
                            
                        logging.error(error_msg)
                        await progress_pipe.put({"status": "error", "message": error_msg})
                    
                    return result_file
                
            except Exception as e:
                logging.error(f"下载任务执行失败: {str(e)}")
                error_msg = f"下载失败: {str(e)}"
                logging.error(error_msg)
                await progress_pipe.put({"status": "error", "message": error_msg})
                return None
            finally:
                # 确保恢复标准输出
                sys.stdout = original_stdout
        
        # 定义进度监控任务
        async def monitor_progress():
            try:
                # 启动下载任务
                file_path = await download_task()
                
                if not file_path or not os.path.exists(file_path):
                    error_msg = "下载失败，未找到文件"
                    logging.error(error_msg)
                    await progress_pipe.put({"status": "error", "message": error_msg})
                    return None
                
                # 下载完成，准备文件传输
                file_name = os.path.basename(file_path)
                file_size = os.path.getsize(file_path)
                
                await progress_pipe.put({
                    "status": "complete", 
                    "progress": 100, 
                    "message": "下载完成",
                    "file_name": file_name,
                    "file_size": file_size,
                    "file_path": file_path
                })
                
                return file_path
            except Exception as e:
                logging.error(f"进度监控任务失败: {str(e)}")
                error_msg = f"下载监控失败: {str(e)}"
                logging.error(error_msg)
                await progress_pipe.put({"status": "error", "message": error_msg})
                return None
        
        # 定义流式响应生成器
        async def progress_stream():
            try:
                # 启动监控任务
                monitor_task = asyncio.create_task(monitor_progress())
                
                # 发送进度更新
                while True:
                    try:
                        # 等待进度更新，设置超时避免无限等待
                        progress_data = await asyncio.wait_for(progress_pipe.get(), timeout=1.0)
                        
                        # 发送进度数据 - 确保转义所有换行符
                        progress_data_str = json.dumps(progress_data).replace('\n', '\\n')
                        yield f"data: {progress_data_str}\n\n"
                        
                        # 如果下载完成或出错，结束流
                        if progress_data.get("status") in ["complete", "error"]:
                            file_path = await monitor_task
                            if file_path:
                                # 确保文件存在
                                if not os.path.exists(file_path):
                                    logging.error(f"准备下载的文件不存在: {file_path}")
                                    # 尝试查找同名文件（处理路径中的特殊字符问题）
                                    dir_path = os.path.dirname(file_path)
                                    if os.path.exists(dir_path) and os.path.isdir(dir_path):
                                        base_name = os.path.basename(file_path)
                                        # 列出目录中的所有文件
                                        dir_files = os.listdir(dir_path)
                                        logging.info(f"目录 {dir_path} 中的文件: {dir_files}")
                                        
                                        # 尝试查找相似文件名 - 改进匹配算法
                                        for file in dir_files:
                                            # 检查文件名前缀匹配或者文件扩展名相同
                                            base_ext = os.path.splitext(base_name)[1]
                                            file_ext = os.path.splitext(file)[1]
                                            
                                            if (file.startswith(base_name[:5]) or 
                                                base_name.startswith(file[:5]) or 
                                                (base_ext and base_ext == file_ext)):
                                                # 找到可能匹配的文件
                                                potential_file = os.path.join(dir_path, file)
                                                if os.path.isfile(potential_file):
                                                    logging.info(f"找到可能匹配的文件: {potential_file}")
                                                    file_path = potential_file
                                                    break
                                
                                # 再次检查文件是否存在
                                if not os.path.exists(file_path):
                                    # 检查是否是多P视频下载的情况
                                    # 检查临时目录中是否有文件，以及文件名是否包含分P标记（如[01]）
                                    is_multipart = False
                                    if os.path.exists(temp_dir):
                                        for root, _, files in os.walk(temp_dir):
                                            for file in files:
                                                if '[' in file and ']' in file and file.lower().endswith(('.mp4', '.flv', '.webm')):
                                                    is_multipart = True
                                                    logging.info(f"检测到多P视频文件: {file}")
                                                    break
                                            if is_multipart:
                                                break
                                    
                                    if is_multipart or (os.path.exists(temp_dir) and len(os.listdir(temp_dir)) > 0):
                                        # 尝试查找下载目录中的任何视频文件
                                        video_files = []
                                        logging.info(f"检查临时目录中的文件: {temp_dir}")
                                        for root, dirs, files in os.walk(temp_dir):
                                            logging.info(f"目录 {root} 中的文件: {files}")
                                            for file in files:
                                                if file.lower().endswith(('.mp4', '.flv', '.webm')):
                                                    video_files.append(os.path.join(root, file))
                                                    logging.info(f"找到视频文件: {file}")
                                        
                                        if video_files:
                                            # 按文件大小排序，选择最大的文件（通常是完整视频）
                                            video_files.sort(key=lambda x: os.path.getsize(x) if os.path.exists(x) else 0, reverse=True)
                                            file_path = video_files[0]  # 使用找到的最大视频文件
                                            logging.info(f"选择最大的视频文件: {file_path}, 大小: {os.path.getsize(file_path) if os.path.exists(file_path) else 0} 字节")
                                        else:
                                            # 如果没有找到视频文件，尝试查找任何非零大小的文件
                                            all_files = []
                                            for root, _, files in os.walk(temp_dir):
                                                for file in files:
                                                    file_path = os.path.join(root, file)
                                                    if os.path.exists(file_path) and os.path.getsize(file_path) > 0:
                                                        all_files.append(file_path)
                                            
                                            if all_files:
                                                # 按文件大小排序
                                                all_files.sort(key=lambda x: os.path.getsize(x), reverse=True)
                                                file_path = all_files[0]
                                                logging.info(f"未找到视频文件，使用最大的其他文件: {file_path}")
                                            else:
                                                error_msg = f"下载失败，临时目录中没有找到任何可用文件"
                                                logging.error(error_msg)
                                                continue
                                    else:
                                        error_msg = f"下载失败，未找到文件: {os.path.basename(file_path)}"
                                        logging.error(error_msg)
                                        continue
                                    
                                # 返回文件路径信息，前端将使用这个信息发起文件下载请求
                                
                                # 对文件路径进行URL编码，确保特殊字符和空格能被正确处理
                                encoded_path = urllib.parse.quote(file_path)
                                # 确保SSE响应格式正确，避免换行符问题
                                ready_data = {
                                    'status': 'ready', 
                                    'file_name': os.path.basename(file_path),
                                    'download_url': f'/api/video-downloader/download-file?file_path={encoded_path}'
                                }
                                progress_ready_str = json.dumps(ready_data).replace('\n', '\\n')
                                yield f"data: {progress_ready_str}\n\n"
                            break
                    except asyncio.TimeoutError:
                        # 超时，发送心跳保持连接
                        progress_heartbeat_str = json.dumps({'status': 'heartbeat'}).replace('\n', '\\n')
                        yield f"data: {progress_heartbeat_str}\n\n"
                        
                        # 检查监控任务是否已完成
                        if monitor_task.done():
                            file_path = monitor_task.result()
                            if file_path and os.path.exists(file_path):
                                # 确保文件存在
                                if not os.path.exists(file_path):
                                    logging.error(f"准备下载的文件不存在: {file_path}")
                                    # 尝试查找同名文件（处理路径中的特殊字符问题）
                                    dir_path = os.path.dirname(file_path)
                                    if os.path.exists(dir_path) and os.path.isdir(dir_path):
                                        base_name = os.path.basename(file_path)
                                        # 列出目录中的所有文件
                                        dir_files = os.listdir(dir_path)
                                        logging.info(f"目录 {dir_path} 中的文件: {dir_files}")
                                        
                                        # 尝试查找相似文件名 - 改进匹配算法
                                        for file in dir_files:
                                            # 检查文件名前缀匹配或者文件扩展名相同
                                            base_ext = os.path.splitext(base_name)[1]
                                            file_ext = os.path.splitext(file)[1]
                                            
                                            if (file.startswith(base_name[:5]) or 
                                                base_name.startswith(file[:5]) or 
                                                (base_ext and base_ext == file_ext)):
                                                # 找到可能匹配的文件
                                                potential_file = os.path.join(dir_path, file)
                                                if os.path.isfile(potential_file):
                                                    logging.info(f"找到可能匹配的文件: {potential_file}")
                                                    file_path = potential_file
                                                    break
                                
                                # 对文件路径进行URL编码，确保特殊字符和空格能被正确处理
                                encoded_path = urllib.parse.quote(file_path)
                                # 确保SSE响应格式正确，避免换行符问题
                                ready_data = {
                                    'status': 'ready', 
                                    'file_name': os.path.basename(file_path),
                                    'download_url': f'/api/video-downloader/download-file?file_path={encoded_path}'
                                }
                                progress_ready_str = json.dumps(ready_data).replace('\n', '\\n')
                                yield f"data: {progress_ready_str}\n\n"
                            break
                    except asyncio.CancelledError:
                        # 客户端断开连接，取消任务并清理资源
                        logging.warning("客户端断开连接，取消下载任务")
                        if not monitor_task.done():
                            monitor_task.cancel()
                            
                        # 尝试清理临时文件
                        try:
                            if os.path.exists(temp_dir):
                                shutil.rmtree(temp_dir, ignore_errors=True)
                                logging.info(f"已清理临时下载目录: {temp_dir}")
                        except Exception as e:
                            logging.error(f"清理临时目录失败: {str(e)}")
                            
                        # 尝试发送取消状态消息
                        try:
                            progress_cancelled_str = json.dumps({'status': 'cancelled', 'message': '下载已取消'}).replace('\n', '\\n')
                            yield f"data: {progress_cancelled_str}\n\n"
                        except:
                            pass
                            
                        raise  # 重新抛出异常以便正确处理
            except asyncio.CancelledError:
                # 处理取消异常
                logging.warning("流式下载被取消")
                # 不生成更多数据，让异常传播以便正确清理资源
                try:
                    # 尝试清理临时文件
                    if os.path.exists(temp_dir):
                        shutil.rmtree(temp_dir, ignore_errors=True)
                        logging.info(f"已清理临时下载目录: {temp_dir}")
                except Exception as e:
                    logging.error(f"清理临时目录失败: {str(e)}")
                    
                try:
                    # 在取消前尝试发送一个取消状态消息
                    progress_cancelled_str = json.dumps({'status': 'cancelled', 'message': '下载已取消，资源已清理'}).replace('\n', '\\n')
                    yield f"data: {progress_cancelled_str}\n\n"
                except:
                    pass
                raise
            except Exception as e:
                logging.error(f"进度流生成失败: {str(e)}")
                progress_error_str = json.dumps({'status': 'error', 'message': str(e)}).replace('\n', '\\n')
                yield f"data: {progress_error_str}\n\n"
            finally:
                # 流结束，确保发送结束信号
                try:
                    progress_end_str = json.dumps({'status': 'end'}).replace('\n', '\\n')
                    yield f"data: {progress_end_str}\n\n"
                except asyncio.CancelledError:
                    # 忽略在finally块中的取消异常
                    pass
        
        # 定义更可靠的清理函数
        def cleanup_resources():
            try:
                # 确保临时目录被删除
                if os.path.exists(temp_dir):
                    logging.info(f"清理临时下载目录: {temp_dir}")
                    shutil.rmtree(temp_dir, ignore_errors=True)
                    
                # 如果监控任务还在运行，取消它
                if 'monitor_task' in locals() and not monitor_task.done():
                    try:
                        monitor_task.cancel()
                        logging.info("已取消下载监控任务")
                    except Exception as e:
                        logging.error(f"取消监控任务失败: {str(e)}")
            except Exception as e:
                logging.error(f"清理资源失败: {str(e)}")
            finally:
                # 确保所有资源都被释放
                logging.info("下载任务资源清理完成")
                # 重置sys.argv以防止影响其他功能
                if 'original_argv' in locals():
                    sys.argv = original_argv
        
        # 返回SSE流式响应
        return StreamingResponse(
            progress_stream(),
            media_type="text/event-stream",
            background=BackgroundTask(cleanup_resources)
        )
    except Exception as e:
        error_msg = f"流式下载初始化失败: {str(e)}"
        logging.error(error_msg)
        # 提供更详细的错误信息
        if "Permission denied" in str(e):
            raise HTTPException(status_code=500, detail=f"创建临时目录失败，权限被拒绝: {str(e)}")
        elif "disk space" in str(e).lower() or "no space" in str(e).lower():
            raise HTTPException(status_code=500, detail=f"磁盘空间不足，无法创建临时文件: {str(e)}")
        else:
            raise HTTPException(status_code=500, detail=error_msg)

@router.get("/download-file")
@router.head("/download-file")
async def download_file(file_path: str, response: Response, request: Request):
    """提供文件下载，支持GET和HEAD请求"""
    file_handle = None
    try:
        # URL解码文件路径
        import urllib.parse
        decoded_path = urllib.parse.unquote(file_path)
        logging.info(f"尝试下载文件: {decoded_path}，请求方法: {request.method}")
        
        # 安全检查：确保路径存在且是文件
        if not os.path.exists(decoded_path):
            logging.error(f"文件不存在: {decoded_path}")
            
            # 尝试查找同名文件（处理路径中的特殊字符问题）
            dir_path = os.path.dirname(decoded_path)
            if os.path.exists(dir_path) and os.path.isdir(dir_path):
                base_name = os.path.basename(decoded_path)
                # 列出目录中的所有文件
                try:
                    dir_files = os.listdir(dir_path)
                    logging.info(f"目录 {dir_path} 中的文件: {dir_files}")
                    
                    # 检查是否是多P视频下载目录
                    is_multipart = False
                    for file in dir_files:
                        if '[' in file and ']' in file and file.lower().endswith(('.mp4', '.flv', '.webm')):
                            is_multipart = True
                            logging.info(f"检测到多P视频文件: {file}")
                            break
                    
                    # 尝试查找相似文件名 - 改进匹配算法
                    if is_multipart:
                        # 对于多P视频，选择最大的视频文件
                        video_files = []
                        for file in dir_files:
                            if file.lower().endswith(('.mp4', '.flv', '.webm')):
                                file_path = os.path.join(dir_path, file)
                                try:
                                    if os.path.isfile(file_path) and os.path.getsize(file_path) > 0:
                                        video_files.append(file_path)
                                except (PermissionError, OSError) as e:
                                    logging.warning(f"无法访问文件 {file_path}: {str(e)}")
                                    continue
                        
                        if video_files:
                            # 按文件大小排序
                            try:
                                video_files.sort(key=lambda x: os.path.getsize(x) if os.path.exists(x) else 0, reverse=True)
                                decoded_path = video_files[0]
                                logging.info(f"多P视频：选择最大的视频文件: {decoded_path}, 大小: {os.path.getsize(decoded_path)} 字节")
                            except Exception as e:
                                logging.error(f"排序视频文件时出错: {str(e)}")
                                # 如果排序失败，使用第一个文件
                                if video_files:
                                    decoded_path = video_files[0]
                                    logging.info(f"使用第一个找到的视频文件: {decoded_path}")
                    else:
                        # 常规匹配逻辑
                        for file in dir_files:
                            # 检查文件名前缀匹配或者文件扩展名相同
                            base_ext = os.path.splitext(base_name)[1]
                            file_ext = os.path.splitext(file)[1]
                            
                            if (file.startswith(base_name[:5]) or 
                                base_name.startswith(file[:5]) or 
                                (base_ext and base_ext == file_ext)):
                                # 找到可能匹配的文件
                                potential_file = os.path.join(dir_path, file)
                                try:
                                    if os.path.isfile(potential_file):
                                        logging.info(f"找到可能匹配的文件: {potential_file}")
                                        decoded_path = potential_file
                                        break
                                except (PermissionError, OSError) as e:
                                    logging.warning(f"无法访问文件 {potential_file}: {str(e)}")
                                    continue
                except (PermissionError, OSError) as e:
                    logging.error(f"无法列出目录 {dir_path} 中的文件: {str(e)}")
            
            # 再次检查文件是否存在
            if not os.path.exists(decoded_path):
                # 尝试更灵活的文件名匹配
                found = False
                if os.path.exists(dir_path) and os.path.isdir(dir_path):
                    try:
                        # 尝试通过部分文件名匹配
                        for file in os.listdir(dir_path):
                            file_path = os.path.join(dir_path, file)
                            try:
                                if os.path.isfile(file_path) and file.endswith(os.path.splitext(base_name)[1]):
                                    # 找到相同扩展名的文件
                                    decoded_path = file_path
                                    found = True
                                    logging.info(f"通过扩展名匹配找到文件: {decoded_path}")
                                    break
                            except (PermissionError, OSError) as e:
                                logging.warning(f"无法访问文件 {file_path}: {str(e)}")
                                continue
                    except (PermissionError, OSError) as e:
                        logging.error(f"无法列出目录 {dir_path} 中的文件: {str(e)}")
                
                if not found:
                    raise HTTPException(status_code=404, detail=f"文件不存在或已被删除: {os.path.basename(decoded_path)}")
                    
                # 记录更多信息以便调试
                logging.info(f"最终使用的文件路径: {decoded_path}")
        
        try:
            if not os.path.isfile(decoded_path):
                logging.error(f"路径不是文件: {decoded_path}")
                raise HTTPException(status_code=400, detail="提供的路径不是文件")
        except (PermissionError, OSError) as e:
            logging.error(f"检查文件路径时出错: {str(e)}")
            raise HTTPException(status_code=500, detail=f"检查文件路径时出错: {str(e)}")
        
        # 获取文件名并处理特殊字符
        try:
            filename = os.path.basename(decoded_path)
            # 确保文件名是有效的，处理特殊字符
            encoded_filename = urllib.parse.quote(filename)
            logging.info(f"文件名: {filename}, 编码后: {encoded_filename}")
        except Exception as e:
            logging.error(f"处理文件名时出错: {str(e)}")
            # 使用安全的默认文件名
            filename = f"download_{int(time.time())}.mp4"
            encoded_filename = urllib.parse.quote(filename)
            logging.info(f"使用默认文件名: {filename}")

        
        # 定义更可靠的清理函数
        def cleanup():
            try:
                # 确保文件句柄已关闭
                if file_handle and not file_handle.closed:
                    file_handle.close()
                    
                # 删除文件
                if os.path.exists(decoded_path):
                    os.remove(decoded_path)
                    
                # 尝试删除父目录（如果是临时目录）
                parent_dir = os.path.dirname(decoded_path)
                if "video_download_" in parent_dir and os.path.exists(parent_dir):
                    shutil.rmtree(parent_dir, ignore_errors=True)
                    
                logging.info(f"临时文件清理完成: {decoded_path}")
            except Exception as e:
                logging.error(f"清理临时文件失败: {str(e)}")
        
        # 打开文件
        try:
            # 确保文件路径是有效的
            try:
                # 检查文件是否可访问
                with open(decoded_path, "rb") as test_file:
                    # 只读取一小部分确认文件可访问
                    test_file.read(1)
                # 文件可访问，现在正式打开
                file_handle = open(decoded_path, "rb")
            except (PermissionError, FileNotFoundError) as e:
                logging.error(f"文件访问错误: {str(e)}")
                raise HTTPException(status_code=403 if isinstance(e, PermissionError) else 404, 
                                    detail=f"文件访问错误: {os.path.basename(decoded_path)}")
            except Exception as e:
                logging.error(f"打开文件失败: {str(e)}")
                raise HTTPException(status_code=500, detail=f"打开文件失败: {str(e)}")
        except HTTPException:
            # 重新抛出HTTP异常
            raise
        except Exception as e:
            logging.error(f"打开文件失败: {str(e)}")
            raise HTTPException(status_code=500, detail=f"打开文件失败: {str(e)}")
        
        # 返回文件响应
        try:
            # 处理HEAD请求 - 只返回头信息，不返回文件内容
            if request.method == "HEAD":
                # 获取文件大小
                file_size = os.path.getsize(decoded_path)
                # 关闭文件句柄，HEAD请求不需要传输文件内容
                if file_handle and not file_handle.closed:
                    file_handle.close()
                    file_handle = None
                
                # 设置响应头
                response.headers["Content-Type"] = "application/octet-stream"
                response.headers["Content-Disposition"] = f'attachment; filename*=UTF-8\'\'{encoded_filename}'
                response.headers["Content-Length"] = str(file_size)
                return Response(status_code=200)
            
            # 处理GET请求 - 返回文件内容
            return StreamingResponse(
                file_handle,
                media_type="application/octet-stream",
                headers={
                    "Content-Disposition": f'attachment; filename*=UTF-8\'\'{encoded_filename}'
                },
                background=BackgroundTask(cleanup)
            )
        except Exception as e:
            # 确保文件句柄关闭
            if file_handle and not file_handle.closed:
                file_handle.close()
            logging.error(f"创建StreamingResponse失败: {str(e)}")
            raise HTTPException(status_code=500, detail=f"文件下载失败: {str(e)}")
    except Exception as e:
        error_msg = f"文件下载失败: {str(e)}"
        logging.error(error_msg)
        # 提供更详细的错误信息
        if "Permission denied" in str(e):
            raise HTTPException(status_code=500, detail=f"文件访问权限被拒绝，请检查文件权限: {os.path.basename(file_path)}")
        elif "No such file" in str(e):
            raise HTTPException(status_code=404, detail=f"文件不存在或已被删除: {os.path.basename(file_path)}")
        else:
            raise HTTPException(status_code=500, detail=error_msg)